package com.googlecode.tidej.client.editor;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.NoSuchElementException;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.TextBox;
import com.googlecode.tidej.client.Console;
import com.googlecode.tidej.client.Dialog;
import com.googlecode.tidej.client.codemirror.CodeMirror;
import com.googlecode.tidej.client.codemirror.CodeMirror.Pos;
import com.googlecode.tidej.shared.model.Artifact;
import com.googlecode.tidej.shared.model.Library;
import com.googlecode.tidej.shared.model.Operation;
import com.googlecode.tidej.shared.model.Type;


public class SearchAndReplaceDialog extends Dialog {
  TextBox searchBox = new TextBox();
  TextBox replaceBox = new TextBox();
  ArtifactEditor artifactEditor;
  Artifact artifact;
  Operation current;
  Iterator<Operation> operationIterator;
  int currentPos;
  Button replaceButton = new Button("Replace/Search");
  
  public SearchAndReplaceDialog(ArtifactEditor artifactEditor) {
    super("Search / Replace");

    this.artifactEditor = artifactEditor;
    artifact = artifactEditor.getArtifact();
    
    FlexTable table = new FlexTable();
    
    table.setText(0, 0, "Search:");
    table.setWidget(0, 1, searchBox);

    table.setText(1, 0, "Replace with:");
    table.setWidget(1, 1, replaceBox);
    
    Button searchButton = new Button("Search");
    searchButton.addClickHandler(new ClickHandler() {
      @Override
      public void onClick(ClickEvent event) {
        searchNext();
      }
    });
    addButton(searchButton);

    replaceButton.addClickHandler(new ClickHandler() {
      @Override
      public void onClick(ClickEvent event) {
        replace();
        searchNext();
      }
    });
    addButton(replaceButton);
    replaceButton.setEnabled(false);

    if (artifact instanceof Operation) {
      current = (Operation) artifact;
      currentPos = -1;
    } else if (artifact instanceof Type) {
      operationIterator = getOperationIterator((Type) artifact);
    } else if (artifact instanceof Library) {
      operationIterator = new LibraryOperationIterator((Library) artifact);
    } else {
      throw new IllegalArgumentException();
    }
   
    getContentPanel().add(table);
  }
  

  public void run() {
    show();
    center();
  }

  private void replace() {
    CodeMirror codeMirror = artifactEditor.operationDetailWidget.codeEditor.codeMirror;
    codeMirror.replaceSelection(replaceBox.getText());
    artifactEditor.save();
    currentPos += replaceBox.getText().length() - 1;
  }
  
  private void searchNext() {
    String searchText = searchBox.getText();

    currentPos = current == null ? -1 : current.getBody().indexOf(searchText, currentPos + 1);
    if (currentPos == -1) {
      do {
        if (operationIterator == null || !operationIterator.hasNext()) {
          replaceButton.setEnabled(false);
          return;
        }
        current = operationIterator.next();
      } while (current.getBody().indexOf(searchText) == -1);
      artifactEditor.controller.select(current);
      new Timer() {
        public void run() {
          searchNext();
        }}.schedule(100);
      return;
    } 
    replaceButton.setEnabled(true);
    
    CodeMirror codeMirror = artifactEditor.operationDetailWidget.codeEditor.codeMirror;
    
    Pos start = codeMirror.posFromIndex(currentPos);
    Pos end = codeMirror.posFromIndex(currentPos + searchText.length());
    
    codeMirror.setSelection(start, end);
  }
  
  static Iterator<Operation> getOperationIterator(Type type) {
    ArrayList<Operation> operations = new ArrayList<Operation>();
    if (type.getConstructor() != null) {
      operations.add(type.getConstructor());
    }
    operations.addAll(type.getOwnOperations());
    return operations.iterator();
  }
  
  static class LibraryOperationIterator implements Iterator<Operation> {
    Iterator<Type> typeIterator;
    Iterator<Operation> operationIterator;

    LibraryOperationIterator(Library lib) {
      ArrayList<Type> types = new ArrayList<Type>();
      if (lib.getGlobals() != null) {
        types.add(lib.getGlobals());
      }
      types.addAll(lib.getTypes());
      typeIterator = types.iterator();
    }
    
    public Operation next() {
      if (!hasNext()) {
        throw new NoSuchElementException();
      }
      return operationIterator.next();
    }
    
    public boolean hasNext() {
      while(true) {
        Console.log("operationIterator: " + operationIterator + " typeIterator: " + typeIterator);
        if (operationIterator != null && operationIterator.hasNext()) {
          Console.log("No next for operationIterato");
          return true;
        }
        if (!typeIterator.hasNext()) {
          Console.log("No next for typeIterato");
          return false;
        }
        operationIterator = getOperationIterator(typeIterator.next());
      }
    }

    @Override
    public void remove() {
      throw new UnsupportedOperationException();
    }
  }
  
  
}
